package com.mbg17.service.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.mbg17.dao.SystemInfoMapper;
import com.mbg17.dao.TaskMapper;
import com.mbg17.dao.TaskSuiteMapper;
import com.mbg17.dao.TestCaseMapper;
import com.mbg17.domain.*;
import com.mbg17.service.service.TaskReportService;
import com.mbg17.service.service.TestRecordService;
import com.mbg17.utils.AssertUtil;
import com.mbg17.utils.JDBCUtil;
import com.mbg17.utils.RestAssuredUtils;
import com.mbg17.utils.StringUtils;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Component
@RabbitListener(queues = "interface")
@Slf4j
public class TaskActuator {
    @Resource
    private TestRecordService testRecordService;

    @Resource
    private SystemInfoMapper systemInfoMapper;

    @Resource
    private TaskMapper taskMapper;

    @Resource
    private TaskSuiteMapper taskSuiteMapper;

    @Resource
    private TestCaseMapper testCaseMapper;

    @Resource
    private TaskReportService taskReportService;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @RabbitHandler
    public void runInterfaceTask(Integer taskRecordId){
        log.info("----------------------任务id({})开始执行----------------------",taskRecordId);
        // 获取任务记录
        TestRecord testRecordById = testRecordService.getTestRecordById(taskRecordId);
        // 获取任务详情
        Task taskById = testRecordById ==null?null:taskMapper.getTaskById(testRecordById.getTaskId());
        // 判断是否存在或归档
        if(taskById==null || taskById.getIsPigeonhole()){
            log.info("任务记录不存在或任务套件已归档，执行结束");
            return;
        }
        // 获取系统信息
        SystemInfo systemInfoById = systemInfoMapper.getSystemInfoById(testRecordById.getSystemInfoId());
        // 获取所有测试套件
        List<TaskSuit> taskSuiteByTaskAndSystemId = taskSuiteMapper.getTaskSuiteByTaskAndSystemId(testRecordById.getTaskId(), testRecordById.getSystemId());
        if(taskSuiteByTaskAndSystemId!=null && !taskSuiteByTaskAndSystemId.isEmpty() && systemInfoById !=null){
            // 遍历执行套件
            for (TaskSuit taskSuit : taskSuiteByTaskAndSystemId) {
                Boolean flag = true;
                TaskReport taskReport = new TaskReport(0,taskRecordId,taskSuit.getSuiteName()+"套件开始执行");
                taskReportService.addTaskReport(taskReport);
                log.info("----------------------({})套件开始执行----------------------",taskSuit.getSuiteName());
                List<TestCase> allTestCaseByTestSuitId = testCaseMapper.getAllTestCaseByTestSuitId(testRecordById.getSystemId(), testRecordById.getTaskId(), taskSuit.getId());
                for (TestCase testCase : allTestCaseByTestSuitId) {
                    TaskReport innerTaskReport = new TaskReport(taskReport.getId(), taskRecordId, testCase.getCaseName());
                    Boolean innerFlag = true;
                    StringBuilder report = new StringBuilder();
                    log.info("正在执行测试用例:用例名称为({})",testCase.getCaseName());
                    // 使用fastjson解析请求体
                    ApiTestInputDto apiTestInputDto = JSONObject.parseObject(testCase.getRequestBody(), ApiTestInputDto.class);
                    // 请求体参数化替换
                    apiTestInputDto = StringUtils.processTemplate(apiTestInputDto,redisTemplate,taskRecordId);
                    // 构造请求对象
                    RequestSpecification requestSpecification = RestAssuredUtils.requestEncapsulates(apiTestInputDto);
                    Response response = null;
                    String res = null;
                    try{
                        Method method = RestAssuredUtils.class.getMethod(testCase.getInterFace().getRequest(), String.class, RequestSpecification.class);
                        response = (Response)method.invoke(null, systemInfoById.getAddress() + testCase.getInterFace().getPath(), requestSpecification);
                        res = response.asString();
                        if(testCase.getInterFace().getResponse().toLowerCase().equals("xml")){
                            String xml = StringUtils.xmlToJson(response.asString());
                            if(xml!=null && !xml.equals("{}")){
                                res = xml;
                            }else {
                                log.error("报文xml转json失败,保留原始报文");
                            }
                        }
                        log.info("执行成功，响应结果为:{}",res);
                    }catch (Exception e){
                        flag =false;
                        report.append("请求方法定义错误，继续下一个测试用例! \r\n");
                        log.error("请求方法定义错误，继续下一个测试用例！");
                        continue;
                    }
                    DocumentContext parse = JsonPath.parse(res);
                    // 提取数据
                    StringUtils.cacheParams(response,apiTestInputDto,redisTemplate,taskRecordId,parse);
                    // 响应断言
                    for (Datas anAssert : apiTestInputDto.getAsserts()) {
                        // 校验参数化
                        anAssert.setValue(StringUtils.processTemplate(anAssert.getValue(),redisTemplate,taskRecordId));
                        try{
                            Object read = parse.read(anAssert.getKey());
                            log.info("({})提取数据成功,值为信息为:({})",anAssert.getKey(),read);
                            Method method = AssertUtil.class.getMethod(anAssert.getAssertType(), String.class, String.class);
                            boolean invoke =  (boolean)method.invoke(null, String.valueOf(read), anAssert.getValue());
                            log.info("({})数据比对结果为:({}),期望结果为:({}),实际结果为:({})",anAssert.getKey(), invoke ?"成功":"失败",read,anAssert.getValue());
                            if(!invoke){
                                report.append("("+anAssert.getKey()+")数据比对结果为:(失败),期望结果为:("+read+"}),实际结果为:("+anAssert.getValue()+")！ \r\n");
                                innerFlag =false;
                                flag = false;
                            }
                        }catch (Exception e){
                            innerFlag =false;
                            flag = false;
                            report.append("提取失败否则比较方法错误！ \r\n");
                            log.error("({})提取数据比对错误,错误信息为:({})",anAssert.getKey(),"提取失败否则比较方法错误！");
                        }
                    }
                    // 获取数据库链接
                    Connection connection = null;
                    Statement statement = null;
                    // 数据库断言
                    if(!apiTestInputDto.getDatabaseAsserts().isEmpty()){
                        try{
                            connection = JDBCUtil.getConnection(systemInfoById.getDatabaseUrl(),systemInfoById.getUsername(),systemInfoById.getPassword(),systemInfoById.getType());
                            statement = connection.createStatement();
                            for (Datas extract : apiTestInputDto.getDatabaseAsserts()) {
                                extract.setValue(StringUtils.processTemplate(extract.getValue(),redisTemplate,taskRecordId));
                                Method method = AssertUtil.class.getMethod(extract.getAssertType(), String.class, String.class);
                                ResultSet resultSet = statement.executeQuery(extract.getKey());
                                ArrayList<Object> objects = new ArrayList<>();
                                while (resultSet.next()){
                                    objects.add(resultSet.getString(1));
                                }
                                Object[] ans = objects.toArray();
                                boolean invoke = (boolean)method.invoke(null, Arrays.toString(ans),extract.getValue());
                                log.info("({})数据数据库比对结果为:({}),期望结果为:({}),实际结果为:({})",extract.getKey(), invoke ?"成功":"失败",extract.getValue(),Arrays.toString(ans));
                                if(!invoke){
                                    report.append("("+extract.getKey()+")数据比对结果为:(失败),期望结果为:("+extract.getValue()+"}),实际结果为:("+Arrays.toString(ans)+")！ \r\n");
                                    innerFlag =false;
                                    flag = false;
                                }
                            }
                        }catch (Exception e){
                            report.append("数据库链接失,("+testCase.getCaseName()+")执行失败！ \r\n");
                            innerFlag =false;
                            flag = false;
                            log.error("数据库连接失败，任务({}):数据库断言失败",testCase.getCaseName());
                        }finally {
                            log.info("关闭数据库连接");
                            JDBCUtil.close(statement, connection);
                        }
                    }
                    innerTaskReport.setErrorMsg(report.length()==0?"无":report.toString());
                    if(!innerFlag){
                        innerTaskReport.setStatus(1);
                    }
                    taskReportService.addTaskReport(innerTaskReport);
                }
                log.info("----------------------({})套件执行结束----------------------",taskSuit.getSuiteName());
                if(!flag){
                    taskReport.setStatus(1);
                    taskReportService.updateTaskReport(taskReport);
                }
            }
        }
        testRecordService.updateStatusByTaskIdInteger(taskRecordId);
        log.info("----------------------任务id({})执行结束----------------------",taskRecordId);
    }

    public static void main(String[] args) {
        try {
           Connection connection = JDBCUtil.getConnection("jdbc:mysql://sh-cdb-jqxy09v3.sql.tencentcdb.com:60282/apitest?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true","root","luyuan171102","com.mysql.cj.jdbc.Driver");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
